package com.server.thisService.common.mongo;

import com.project.thisCommon.common.CommonFunction;
import com.server.thisService.common.enums.ConstString;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.project.thisCommon.common.ConvertEntity;
import org.apache.commons.lang.StringUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class MongoExecutor {
    @Autowired
    public MongoTemplate mongoTemplate;

    public <T> MongoBaseEntity<T> insert(String name, T entity){
        MongoCollection collect = createCollection(name);
        MongoBaseEntity<T> mongo = new MongoBaseEntity<>();
        mongo.setBody(entity);
        mongo.setTableName(name);
        mongo.setStatus(ConstString.YES);
        mongo.setCreateTime(CommonFunction.GetNowDateString());
        collect.insertOne(Document.parse(ConvertEntity.javaToJson(mongo)));
        return mongo;
    }

    public <T> MongoBaseEntity<T> insert(T entity){
        return insert(entity.getClass().getName(), entity);
    }

    public <T> List<T> search(T query, Type tp, MongoPageEntity page){
        if (null == page){
            page = new MongoPageEntity();
        }
        return search(query.getClass().getName(), query, tp, page);
    }

    public <T> List<T> search(T query, Type tp){
        return search(query, tp, new MongoPageEntity());
    }

    public <T> List<T> search(String name, T query, Type tp, MongoPageEntity page){
        List<T> result = new ArrayList<>();
        MongoCollection collect = createCollection(name);
        BasicDBObject obj = createMongoBase(name, query);
        FindIterable list = collect.find(Document.parse(obj.toJson()));
        if (page.getSorts().size() > 0){
            BasicDBObject sort = new BasicDBObject();
            page.getSorts().forEach((k, v)->sort.put(k, v));
            list = list.sort(sort);
        }
        if (page.getIndex() > 0){
            list = list.skip(page.getIndex());
        }
        if (page.getSize() > 0){
            list = list.limit(page.getSize());
        }
        MongoCursor<Document> cursor = list.iterator();
        while(cursor.hasNext()){
            String json = cursor.next().toJson();
            MongoBaseEntity<T> jsonObj = ConvertEntity.jsonToJava(json, tp);
            result.add(jsonObj.getBody());
        }

        return result;
    }

    public <T> void update(T query, T entity){
        update(query.getClass().getName(), query, entity);
    }

    public <T> void update(String name, T query, T entity){
        MongoCollection collect = createCollection(name);
        BasicDBObject queryObj = createMongoBase(name, query);
        BasicDBObject editObj = createMongoBase(name, entity);
        collect.updateMany(Document.parse(queryObj.toJson()), Document.parse(editObj.toJson()));
    }

    protected <T> BasicDBObject createMongoBase(String name, T query){
        MongoBaseEntity<T> queryMongo = new MongoBaseEntity<>();
        queryMongo.setTableName(name);
        queryMongo.setStatus(ConstString.YES);
        queryMongo.setBody(query);
        BasicDBObject obj = convertMongo(queryMongo);
        return obj;
    }

    protected MongoCollection createCollection(String cls){
        if (!mongoTemplate.collectionExists(cls)){
            mongoTemplate.createCollection(cls);
        }

        return mongoTemplate.getCollection(cls);
    }

    protected BasicDBObject convertMongo(Object entity){
        BasicDBObject dbObject = new BasicDBObject();
        convertMongo(StringUtils.EMPTY, ConvertEntity.CovertToMap(entity), dbObject);
        return dbObject;
    }

    protected void convertMongo(String key, Map<String, Object> entity, BasicDBObject dbObject){
        for(Map.Entry<String, Object> en : entity.entrySet()){
            String keyVal = StringUtils.isEmpty(key) ? en.getKey() : String.format("%s.%s", key, en.getKey());
            if (en.getValue() instanceof Map){
                convertMongo(keyVal, (Map<String, Object>) en.getValue(), dbObject);
            } else if (en.getValue() instanceof List){
                convertMongoList(keyVal, (List) en.getValue(), dbObject);
            } else {
                dbObject.put(keyVal, en.getValue());
            }
        }
    }

    protected void convertMongoList(String key, List list, BasicDBObject dbObject){
        for(Object item : list){
            if (item instanceof Map){
                convertMongo(key, (Map<String, Object>) item, dbObject);
            } else if(item instanceof List){
                convertMongoList(key, (List) item, dbObject);
            } else {
                BasicDBObject basicDBList = createMongoIn(list);
                dbObject.put(key, basicDBList);
                break;
            }
        }
    }

    protected BasicDBObject createMongoIn(List list){
        BasicDBList basicDBList = new BasicDBList();
        for(Object item : list){
            basicDBList.add(item);
        }
        BasicDBObject basicDBObject = new BasicDBObject();
        basicDBObject.put("$in", basicDBList);
        return basicDBObject;
    }
}
